/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.hop.core;

import org.apache.batik.ext.awt.RenderingHintsKeyExt;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.Map;
import java.util.TreeMap;

/**
 * Universal image storage for Swing processing. It contains SVG or bitmap image depends on file and
 * settings.
 */
public abstract class SwingUniversalImage {

  private Map<String, BufferedImage> cache = new TreeMap<>();

  public abstract boolean isBitmap();

  /** Just scale for area's size. */
  protected abstract void renderSimple(BufferedImage area);

  /** Render with scale, at specified position, with possible rotation. */
  protected abstract void render(
      Graphics2D gc, int centerX, int centerY, int width, int height, double angleRadians);

  /** Get scaled image. */
  public synchronized BufferedImage getAsBitmapForSize(int width, int height) {
    String key = width + "x" + height;
    BufferedImage result = cache.get(key);
    if (result == null) {
      result = createBitmap(width, height);
      renderSimple(result);
      cache.put(key, result);
    }
    return result;
  }

  /** Draw rotated image on double canvas size. It required against lost corners on rotate. */
  public synchronized BufferedImage getAsBitmapForSize(int width, int height, double angleRadians) {
    int angleDegree = (int) Math.round(Math.toDegrees(angleRadians));
    while (angleDegree < 0) {
      angleDegree += 360;
    }
    angleDegree %= 360;
    angleRadians = Math.toRadians(angleDegree);

    String key = width + "x" + height + "/" + Integer.toString(angleDegree);
    BufferedImage result = cache.get(key);
    if (result == null) {
      result = createDoubleBitmap(width, height);

      Graphics2D gc = createGraphics(result);
      render(gc, result.getWidth() / 2, result.getHeight() / 2, width, height, angleRadians);
      gc.dispose();

      cache.put(key, result);
    }
    return result;
  }

  public synchronized void drawToGraphics(
      Graphics2D gc, int locationX, int locationY, int width, int height) {
    render(gc, locationX + width / 2, locationY + height / 2, width, height, 0);
  }

  public synchronized void drawToGraphics(
      Graphics2D gc, int centerX, int centerY, int width, int height, double angleRadians) {
    render(gc, centerX, centerY, width, height, angleRadians);
  }

  /** Create bitmap with specified size and full colorspace. */
  public static BufferedImage createBitmap(int width, int height) {
    return new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
  }

  /** Create bitmap with double of specified size and full colorspace. Used for rotated images. */
  public static BufferedImage createDoubleBitmap(int width, int height) {
    int sz = Math.max(width, height) * 2;
    return new BufferedImage(sz, sz, BufferedImage.TYPE_INT_ARGB);
  }

  /** Create Graphics2D for specified bitmap with rendering hints. */
  public static Graphics2D createGraphics(BufferedImage area) {
    Graphics2D gc = (Graphics2D) area.getGraphics();
    gc.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
    gc.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    gc.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
    gc.setRenderingHint(
        RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
    gc.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE);
    gc.setRenderingHint(
        RenderingHintsKeyExt.KEY_TRANSCODING, RenderingHintsKeyExt.VALUE_TRANSCODING_PRINTING);
    return gc;
  }
}
